package common

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
	"io/ioutil"
	"time"

	"chess_platform/internal/cache"
	_ "chess_platform/internal/cache/redis"
	"chess_platform/internal/redis"

	"chess_platform/internal/algorithm"
	"chess_platform/internal/ipay"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
	"github.com/spf13/cobra"
	"gopkg.in/mgo.v2"
	"math/rand"
	"os"
	"strconv"
)

//缓存的key
const (
	CacheExpire            = 300 //5分钟
	CacheExpireTenSecond   = 10  //10秒种
	CaptchaCacheExpire     = 120 //验证码缓存时间
	CaptchaSendAgainExpire = 60  //验证码再次发送的时间

	//游戏服公用redis key
	KeyCurSvrGroup    = "CurSvrGroup" //当前服务器组
	KeyGameRecord     = "GAMERECORD-" //游戏录像
	KeyClub           = "ClubKey-"    //俱乐部信息
	KeyOnlineUserInfo = "ONLINEINFO"  //玩家在线信息
	KeyTableInfo      = "TABLEINFO-"  //桌子信息 //redis:不需要缓存管理

	KeyContestList = "ContestList" //能否上下分的用户列表

	//plat服独用cache key
	KeyClientGameList = "ClientGameList-" //redis: pc, client
	KeyPcUserName     = "PcUserName-"
	KeyPbUserName     = "PbUserName-"

	KeyClubInfo         = "ClubInfo-"
	KeyClientUserInfo   = "ClientUserInfo-" //redis: client
	KeyH5Phone          = "H5Phone-"
	KeyPfPhone          = "PfPhone-"
	KeyClientPhone      = "ClientPhone-" //手机短信验证码,不需要做缓存管理
	KeyH5TokenCache     = "H5TokenKey-"
	KeyPcTokenCache     = "PcTokenKey-"
	KeyPfTokenCache     = "PfTokenKey-" //合伙人前端系统
	KeyPbTokenCache     = "PbTokenKey-" //合伙人后台系统
	KeyClientTokenCache = "ClientTokenKey-"
	KeyClientDeviceId   = "ClientDeviceId-" //客户端的设备ID,用以识别是否在用于设备登录
	KeyRuleType         = "RuleType-"       //参数配置,//redis: pc,client,h5
	KeyAgentInfo        = "AgentInfo-"
	KeyPayType          = "PayType-" //支付方式

	KeyClientOpenId = "ClientOpenId-"

	KeyLobbyIp       = "LobbyIp-" //大厅的地址 //mem: pc, client
	KeyLobbyPhpIp    = "LobbyPhpIp-"
	KeyLobbyAllPhpIp = "LobbyAllPhpIp-"

	KeyH5AgentEvaluation = "H5AgentEvaluation-"
	KeyOtherConfig       = "OtherConfig-"    //其它配置, //redis--->mem:pc,client
	KeyGameNotice        = "GameNotice-"     //子游戏大厅里游戏活动公告 //redis--->mem:pc,client
	KeyAgentNotice       = "AgentNotice-"    //子游戏大厅里游戏招募代理公告  //redis--->mem:pc,client
	KeyNotice            = "Notice"          //活动公告,系统公告  //redis-->mem:pc,client
	KeyH5Notice          = "H5Notice"        //H5公告
	KeyH5NoticeId        = "H5Notice-"       //H5公告
	KeyPfNotice          = "H5Notice"        //pf公告
	KeyPfNoticeId        = "H5Notice-"       //pf公告
	KeyRoomCard          = "RoomCard-"       //钻石局数   //redis-->mem: pc, client
	KeyCardList          = "CardList-"       //钻石列表
	KeyStoreList         = "StoreList"       //商城商品列表 //redis-->mem: pc, client
	KeyDiamondCoinList   = "DiamondCoinList" //钻石兑换金币列表 //redis-->mem: pc, client
	KeyHorse             = "Horse-"          //代理助手及游戏客户端跑马灯  //redis--->mem:pc,client
	KeyH5Token           = "H5Token-"        //代理用户微信token
	KeyGameInfo          = "GameInfo-"       //游戏的基本信息

	KeyAllGame = "AllGame" //所有游戏

	SysConfEmail       = "email"        //发送邮件的发送人的邮箱配置
	SysConfAccessLimit = "access_limit" //访问限速
	SysConfEmailExpire = "email_expire" //客户端邮件过期天数
	SysConfSMS         = "sms"          //短信验证码配置
	SysConfOSSKey      = "oss_key"      //OSS密钥
	SysConfOSSConf     = "oss_conf"     //OSS地址配置
	SysConfSecret      = "secret"       //密钥配置
	SysConfRedis       = "rediscfg"     //
	SysConfQRCode      = "qr_code_url"  //代理店铺及个人二维码
	SysConfAgentCheck  = "agent_check"  //代理考核功能
	SysConfDomain      = "domain"       //平台服务器域名
	SysConfOssDomain   = "oss_domain"   //OSS下载的域名
	SysConfMongodb     = "mgo"          //mongodb配置信息
)

// define global variable
var (
	Mgo        *mgo.Session //mongodb
	MemCache   cache.Cache  //不能用于分布式部署
	RedisCache cache.Cache  //可用于分布式部署
	Redis      *redis.RedisPool
	LogPc      *logs.BeeLogger
	LogH5      *logs.BeeLogger
	LogClient  *logs.BeeLogger
	LogPF      *logs.BeeLogger //合伙人系统前端
	LogPB      *logs.BeeLogger //合伙人系统后端

	LogLogin *logs.BeeLogger

	EmailExpire int64
	CityName    map[string]CountyCityProvince
	CityCode    map[string]string
	CityParent  map[string]CityParentCode

	PlatformRunMode string

	URLCount map[string]int64 //统计平台服每个接口访问次数

	ClientIPay *ipay.IpayHelper
	H5IPay     *ipay.IpayHelper

	Sms             SMS                  //阿里巴巴短信设置
	Ec              EmailConf            //发送邮件的发送人的邮箱配置
	Al              AccessLimitConf      //访问限速配置
	Secret          SecretConf           //密钥设置
	OssKey          OssKeyConf           //Oss密钥
	OssConf         OssBasicConf         //OSS地址配置
	RedisConf       RedisBasicConf       //redis地址配置
	QRCode          QRCodeUrlConf        //代理店铺及个人二维码
	MgoConf         MongodbConf          //mongodb配置信息
	AgentCheck      bool                 //代理考核功能是否开启
	Domain          []string             //域名
	OssDomain       string               //OSS下载的域名
	BasePayCallBack string               //支付回调地址(只有域名加端口,详细的要自己拼接)
	H5Algorithm     *algorithm.Algorithm //H5按权重比例获取支付方式
	ClientAlgorithm *algorithm.Algorithm //客户端按权重比例获取支付方式

	PlatformId   int64    //平台服配置ID
	PlatformConf Platform //平台服配置, H5, pc, client,partner,partner_backend五端
	Dead         bool     //用于表示服务器起来是通过脚本拉取还是主动启动的,脚本拉起说明曾经挂了,要发送短信通知相关人员

	Version       string //平台服版本
	ClientIPayPre string //client端爱贝H5支付平台订单前缀
	H5IPayPre     string //h5端爱贝H5支付平台订单前缀
	PfIPayPre     string //合伙人前端(pf)爱贝H5支付平台订单前缀

	Load int64 //平台负载,进程访问次数
)

func init() {

	var err error
	logFilePc := beego.AppConfig.String("log_file_pc")
	logFileH5 := beego.AppConfig.String("log_file_h5")
	logFileClient := beego.AppConfig.String("log_file_client")

	logFilePc = "log/" + logFilePc
	logFileH5 = "log/" + logFileH5
	logFileClient = "log/" + logFileClient

	logLogin := "log/chess_platform_login.log"

	logPF := "log/chess_platform_pf.log" //合伙人系统前端日志
	logPB := "log/chess_platform_pb.log" //合伙人系统后端日志

	if !IsPathExist("log") {
		err := os.Mkdir("log", 0666)
		if err != nil {
			panic(fmt.Sprintf("创建日志目录出错:%v", err))
		}
	}

	LogPc = logs.NewLogger()
	LogPc.EnableFuncCallDepth(true)

	LogH5 = logs.NewLogger()
	LogH5.EnableFuncCallDepth(true)

	LogClient = logs.NewLogger()
	LogClient.EnableFuncCallDepth(true)

	LogLogin = logs.NewLogger()
	LogLogin.EnableFuncCallDepth(true)

	LogPF = logs.NewLogger()
	LogPF.EnableFuncCallDepth(true)

	LogPB = logs.NewLogger()
	LogPB.EnableFuncCallDepth(true)

	if beego.BConfig.RunMode == "dev" {
		LogPc.SetLogger(logs.AdapterConsole)
		LogClient.SetLogger(logs.AdapterConsole)
		LogH5.SetLogger(logs.AdapterConsole)

		LogLogin.SetLogger(logs.AdapterConsole)
		LogPF.SetLogger(logs.AdapterConsole)
		LogPB.SetLogger(logs.AdapterConsole)

	}

	err = LogPc.SetLogger(logs.AdapterFile, `{"filename":"`+logFilePc+`"}`)
	if err != nil {
		panic("pc端的日志文件设置失败,请检查配置" + err.Error())
		os.Exit(1)
	}
	err = LogH5.SetLogger(logs.AdapterFile, `{"filename":"`+logFileH5+`"}`)
	if err != nil {
		panic("h5端的日志文件设置失败,请检查配置" + err.Error())
		os.Exit(1)
	}
	err = LogClient.SetLogger(logs.AdapterFile, `{"filename":"`+logFileClient+`"}`)
	if err != nil {
		panic("client端的日志文件设置失败,请检查配置" + err.Error())
		os.Exit(1)
	}

	err = LogLogin.SetLogger(logs.AdapterFile, `{"filename":"`+logLogin+`"}`)
	if err != nil {
		panic("client端的login日志文件设置失败,请检查配置" + err.Error())
		os.Exit(1)
	}

	err = LogPF.SetLogger(logs.AdapterFile, `{"filename":"`+logPF+`"}`)
	if err != nil {
		panic("partner前端的日志文件设置失败,请检查配置" + err.Error())
		os.Exit(1)
	}

	err = LogPB.SetLogger(logs.AdapterFile, `{"filename":"`+logPB+`"}`)
	if err != nil {
		panic("partner后端的日志文件设置失败,请检查配置" + err.Error())
		os.Exit(1)
	}

	//----------------上面是日志初始化------------

	Version = time.Now().Format("2006-01-02_15:04:05")
	URLCount = make(map[string]int64, 20)

	if beego.BConfig.RunMode == "pro" {
		ClientIPayPre = "ProClient-"
		H5IPayPre = "ProH5-"
		PfIPayPre = "ProPf-"
	} else {
		ClientIPayPre = "DevClient-"
		H5IPayPre = "DevH5-"
		PfIPayPre = "DevPf-"
	}

	LogPc.Debug(fmt.Sprintf("客户端爱贝支付平台订单号前缀:%v", ClientIPayPre))
	LogPc.Debug(fmt.Sprintf("H5端爱贝支付平台订单号前缀:%v", H5IPayPre))
	LogPc.Debug(fmt.Sprintf("合伙人前端(pf)爱贝支付平台订单号前缀:%v", PfIPayPre))

	PlatformRunMode = beego.AppConfig.String("platform_run_mode")

	InitDB()
	InitCmd()

	if InitPlatformConf(false) != nil {
		os.Exit(1)
	}

	//加载系统配置
	if !ReloadConfigFromMysql() {
		panic("从mysql加载配置失败,请检查")
	}
	CityName, CityCode, CityParent = GetCityCode()

	LogPc.Debug("-----------------------初始化内存及redis缓存开始------------------------------")
	MemCache, err = cache.NewCache("memory", `{"interval":60}`)
	if err != nil {
		panic(fmt.Sprintf("初始化内存缓存失败:%v", err))
	}
	redisHost := fmt.Sprintf("%v:%v", RedisConf.Ip, RedisConf.Port)
	RedisCache, err = cache.NewCache("redis", `{"conn":"`+redisHost+`","password":"`+RedisConf.Passwd+`"}`)
	if err != nil {
		panic("redis连接失败,请检查配置或redis服务器:" + err.Error())
	}
	Redis = redis.NewRedisPool(redisHost, RedisConf.Passwd)
	LogPc.Debug("-----------------------初始化内存及redis缓存结束------------------------------")
	/*
		LogPc.Debug("-----------------------初始化mongodb数据库开始------------------------------")
		url := fmt.Sprintf("mongodb://%v:%v", MgoConf.IP, MgoConf.Port)
		LogPc.Debug(fmt.Sprintf("mongodb url:%v", url))
		Mgo, err = mgo.Dial(url)
		if err != nil {
			//panic("mongodb连接失败,请检查配置或mongodb服务器:" + err.Error())
			LogPc.Error("mongodb连接失败,请检查配置或mongodb服务器:" + err.Error())
		}
		LogPc.Debug("-----------------------初始化mongodb数据库结束------------------------------")
	*/

	if InitAlgorithm() != nil {
		os.Exit(1)
	}

}

func InitPlatformConf(isUpdateStatus bool) error {
	//获取平台服务器配置
	pt, err := GetPlatformInfoById(PlatformId)
	if err != nil {
		LogPc.Error(fmt.Sprintf("获取平台服务器配置失败:%v", err.Error()))
		return err
	}
	if pt.Id != PlatformId {
		LogPc.Error(fmt.Sprintf("数据库未配置ID:%v的平台服务器配置", PlatformId))
		return err
	}
	if isUpdateStatus {
		PlatformConf.Status = pt.Status
		PlatformConf.ExecuteTask = pt.ExecuteTask
	} else {
		PlatformConf = pt
	}
	LogPc.Debug(fmt.Sprintf("平台服务器配置:%#v", PlatformConf))
	return nil
}

//初始化按权重比例选择支付方式
func InitAlgorithm() error {
	LogPc.Debug("-----------------------初始化按权重比例选择支付方式开始------------------------------")
	ClientAlgorithm = algorithm.NewAlgorithm()
	pp, err := PayTypeByPlatform(PlatformClient)
	if err != nil {
		LogPc.Error(fmt.Sprintf("获取Client端按比例支付失败:%v", err))
		return err
	}
	for _, v := range pp {
		LogPc.Debug(fmt.Sprintf("客户端支付比例,支付类型(type:%v):%v,支付比例:%v", v.Remark, v.PayType, v.Percent))
		ClientAlgorithm.Add(int(v.PayType), int(v.Percent))
	}
	ClientAlgorithm.BaseSelect()

	H5Algorithm = algorithm.NewAlgorithm()
	ph, err := PayTypeByPlatform(PlatformH5)
	if err != nil {
		LogPc.Error(fmt.Sprintf("获取H5端按比例支付失败:%v", err))
		return err
	}
	for _, v := range ph {
		LogPc.Debug(fmt.Sprintf("H5端支付比例,支付类型(type:%v):%v,支付比例:%v", v.Remark, v.PayType, v.Percent))
		ClientAlgorithm.Add(int(v.PayType), int(v.Percent))
		H5Algorithm.Add(int(v.PayType), int(v.Percent))
	}
	H5Algorithm.BaseSelect()
	LogPc.Debug("-----------------------初始化按权重比例选择支付方式完成------------------------------")
	return nil
}

func InitCmd() {
	LogPc.Debug("-----------------------初始化外部参数开始------------------------------")
	var RootCmd = &cobra.Command{
		Use:   "help",
		Short: "h",
		Long:  `start, stop,version`,
		Run: func(cmd *cobra.Command, args []string) {
		},
	}
	versionCmd := &cobra.Command{
		Use:   "version",
		Short: "chess_platform version",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("version:%v\n", Version)
			os.Exit(0)
		},
	}

	startCmd := &cobra.Command{
		Use:   "start",
		Short: "chess_platform start",
		Run: func(cmd *cobra.Command, args []string) {
			o := NewDBOrm(DBChessSysData)
			if Dead { //被脚本起来了
				LogPc.Error(fmt.Sprintf("进程被重启,服务器id:%v", PlatformId))
				sql := fmt.Sprintf(`UPDATE %v SET restart_count = restart_count + 1 WHERE id = %v`, TablePlatform, PlatformId)
				o.Raw(sql).Exec()
			}
			sql := fmt.Sprintf(`UPDATE %v SET start_time = %v WHERE id = %v`, TablePlatform, time.Now().Unix(), PlatformId)
			o.Raw(sql).Exec()
		},
	}
	startCmd.Flags().BoolVarP(&Dead, "dead", "d", false, "already dead?")
	startCmd.Flags().Int64Var(&PlatformId, "pid", 0, "platform server id")
	RootCmd.AddCommand(startCmd)
	RootCmd.AddCommand(versionCmd)

	if err := RootCmd.Execute(); err != nil {
		LogPc.Error(fmt.Sprintf("初始化外部参数错误:%v", err))
		os.Exit(1)
	}
	LogPc.Debug("-----------------------初始化外部参数结束------------------------------")
}

type EmailConf struct {
	EmailUser     string `json:"email_user"`
	EmailPassword string `json:"email_password"`
}

type AccessLimitConf struct {
	Frequency  int64 `json:"frequency"`
	ExpireTime int64 `json:"expire_time"`
}

type SMS struct {
	AccessKeyIdSms     string `json:"access_key_id_sms"`
	AccessKeySecretSms string `json:"access_key_secret_sms"`
	SmsSignName        string `json:"sms_sign_name"`
	SmsCode            string `json:"sms_code"`
}

type SecretConf struct {
	TokenKey               string `json:"token_key"`
	TokenExpire            int64  `json:"token_expire"`              //token的失效时间
	H5TokenCacheExpire     int64  `json:"h5_token_cache_expire"`     //h5端token存在缓存里的失效的时间
	PfTokenCacheExpire     int64  `json:"pf_token_cache_expire"`     //合伙人前端token存在缓存里的失效的时间
	PbTokenCacheExpire     int64  `json:"pb_token_cache_expire"`     //合伙人后台token存在缓存里的失效的时间
	ClientTokenCacheExpire int64  `json:"client_token_cache_expire"` //client端token存在缓存里的失效的时间
	PcTokenCacheExpire     int64  `json:"pc_token_cache_expire"`     //pc端token存在缓存里的失效的时间
	Md5Key                 string `json:"md5_key"`
	TcpKey                 string `json:"tcp_key"`
}

type OssKeyConf struct {
	AccessKeyIdOss     string `json:"access_key_id_oss"`
	AccessKeySecretOss string `json:"access_key_secret_oss"`
}

type OssBasicConf struct {
	OssUrlRoot    string `json:"oss_url_root"`
	OssBucketName string `json:"oss_bucket_name"`
	OssRoleAcs    string `json:"oss_role_acs"`
	OssRegion     string `json:"oss_region"`
}

type RedisBasicConf struct {
	Ip     string `json:"ip"`
	Port   int64  `json:"port"`
	Passwd string `json:"passwd"`
	Role   int64  `json:"role"`
}

//mongodb的配置
type MongodbConf struct {
	IP       string `json:"ip"`
	Port     int64  `json:"port"`
	Password string `json:"password"`
}

type QRCodeUrlConf struct {
	PbNameUrl string `json:"pb_name_url"` //合伙人系统后台招总代的二维码
	PfNameUrl string `json:"pf_name_url"` //合伙人系统前端招代理的二维码
	NameUrl   string `json:"name_url"`    //代理个人名片二维码包含的url
	ShopUrl   string `json:"shop_url"`    //代理店铺名片二维码包含的url
	LoginUrl  string `json:"login_url"`   //渠道的下载APP的url
}

//查看平台服务器信息
func GetPlatformInfoById(id int64) (Platform, error) {
	sql := fmt.Sprintf(`
		SELECT
			*
		FROM
			%v
		WHERE
			id = %v
	`, TablePlatform, id)
	var p Platform
	o := NewDBOrm(DBChessSysData)
	err := o.Raw(sql).QueryRow(&p)
	if CheckNoExist(err) {
		return p, nil
	}
	return p, err
}

func SystemConfInfoByKey(key string) string {
	sql := fmt.Sprintf(`
		SELECT
			value
		FROM
			%v
		WHERE
			sckey = '%v'
		LIMIT 1
		`, TableSystemConf, key)
	o := NewDBOrm(DBChessSysData)
	var value string
	o.Raw(sql).QueryRow(&value)
	return value
}

type CountyCityProvince struct {
	ProvinceCode string
	CityCode     string
	CountyCode   string
}

//记录上一级
type CityParentCode struct {
	ParentCode string
	ParentType string
}

//城市行政编码从文件里读取到内存里
func GetCityCode() (map[string]CountyCityProvince, map[string]string, map[string]CityParentCode) {
	type Item struct {
		Code  string `json:"code"`
		Name  string `json:"name"`
		Type  string `json:"type"`
		Child []Item `json:"child"`
	}
	type Country struct {
		Province Item `json:"province"`
	}
	cityName := make(map[string]CountyCityProvince, 390)
	cityCode := make(map[string]string, 390)
	cityParent := make(map[string]CityParentCode, 300)

	data, err := ioutil.ReadFile("conf/city.json")
	if err != nil {
		LogPc.Error(err.Error())
		return nil, nil, nil
	}
	var country []Country
	err = json.Unmarshal(data, &country)
	if err != nil {
		LogPc.Error(err.Error())
		return nil, nil, nil
	}

	for _, v := range country {

		//city[v.Province.Code] = v.Province.Name //省
		for _, vv := range v.Province.Child {
			allCode := CountyCityProvince{
				ProvinceCode: v.Province.Code,
				CityCode:     vv.Code,
			}
			cityCode[vv.Code] = vv.Name //市
			cityName[vv.Name] = allCode //反过来也可以查
			cityParent[vv.Code] = CityParentCode{
				ParentCode: v.Province.Code,
				ParentType: v.Province.Type,
			}
			for _, vvv := range vv.Child { //区
				allCode := CountyCityProvince{
					ProvinceCode: v.Province.Code,
					CityCode:     vv.Code,
					CountyCode:   vvv.Code,
				}
				cityCode[vvv.Code] = vvv.Name
				cityName[vvv.Name] = allCode
				cityParent[vvv.Code] = CityParentCode{
					ParentCode: vv.Code,
					ParentType: vv.Type,
				}
			}

		}
	}
	return cityName, cityCode, cityParent
}

func ReloadConfigFromMysql() bool {
	//------------------------以下配置移到mysql--------------------------------------
	value := SystemConfInfoByKey(SysConfEmail)
	if value == "" {
		LogPc.Error(fmt.Sprintf("发送邮件的发送人的邮箱配置不正确,或没有配置,请检查配置"))
		return false
	}
	err := json.Unmarshal([]byte(value), &Ec)
	if err != nil {
		LogPc.Error(fmt.Sprintf("发送邮件的发送人的邮箱配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("发送邮件的发送人的邮箱配置:%#v\n", Ec))
	//------------
	value = SystemConfInfoByKey(SysConfAccessLimit)
	if value == "" {
		LogPc.Error(fmt.Sprintf("访问限速配置不正确,或没有配置,请检查配置"))
		return false
	}
	err = json.Unmarshal([]byte(value), &Al)
	if err != nil {
		LogPc.Error(fmt.Sprintf("访问限速配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("访问限速配置:%#v\n", Al))
	//-----------
	value = SystemConfInfoByKey(SysConfEmailExpire)
	if value == "" {
		LogPc.Error(fmt.Sprintf("客户端邮件过期天数配置不正确,或没有配置,请检查配置"))
		return false
	}
	EmailExpire, err = strconv.ParseInt(value, 10, 64)
	if err != nil {
		LogPc.Error(fmt.Sprintf("客户端邮件过期天数配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("客户端邮件过期天数配置:%#v天\n", EmailExpire))
	EmailExpire = EmailExpire * 24 * 3600
	//-------------
	value = SystemConfInfoByKey(SysConfSecret)
	if value == "" {
		LogPc.Error(fmt.Sprintf("密钥配置不正确,或没有配置,请检查配置"))
		return false
	}
	err = json.Unmarshal([]byte(value), &Secret)
	if err != nil {
		LogPc.Error(fmt.Sprintf("密钥配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("密钥配置:%#v\n", Secret))

	//-------------
	value = SystemConfInfoByKey(SysConfSMS)
	if value == "" {
		LogPc.Error(fmt.Sprintf("短信验证码配置不正确,或没有配置,请检查配置"))
		return false
	}
	err = json.Unmarshal([]byte(value), &Sms)
	if err != nil {
		LogPc.Error(fmt.Sprintf("短信验证码配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("短信验证码配置:%#v\n", Sms))

	//-------------
	value = SystemConfInfoByKey(SysConfSMS)
	if value == "" {
		LogPc.Error(fmt.Sprintf("短信验证码配置不正确,或没有配置,请检查配置"))
		return false
	}
	err = json.Unmarshal([]byte(value), &Sms)
	if err != nil {
		LogPc.Error(fmt.Sprintf("短信验证码配置失败:%v,请检查配置", err))
	}
	LogPc.Debug(fmt.Sprintf("短信验证码配置:%#v\n", Sms))

	//-------------
	value = SystemConfInfoByKey(SysConfOSSKey)
	if value == "" {
		LogPc.Error(fmt.Sprintf("OSS密钥配置不正确,或没有配置,请检查配置"))
		return false
	}
	err = json.Unmarshal([]byte(value), &OssKey)
	if err != nil {
		LogPc.Error(fmt.Sprintf("OSS密钥配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("OSS密钥配置:%#v\n", OssKey))

	//-------------
	value = SystemConfInfoByKey(SysConfOSSConf)
	if value == "" {
		LogPc.Error(fmt.Sprintf("OSS地址配置不正确,或没有配置,请检查配置"))
		return false
	}
	err = json.Unmarshal([]byte(value), &OssConf)
	if err != nil {
		LogPc.Error(fmt.Sprintf("OSS地址配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("OSS地址配置:%#v\n", OssConf))

	//-------------
	value = SystemConfInfoByKey(SysConfRedis)
	if value == "" {
		LogPc.Error(fmt.Sprintf("redis配置不正确,或没有配置,请检查配置"))
		return false
	}
	var tmpRedis []RedisBasicConf
	err = json.Unmarshal([]byte(value), &tmpRedis)
	if err != nil {
		LogPc.Error(fmt.Sprintf("redis地址配置失败:%v,请检查配置", err))
		return false
	}
	if len(tmpRedis) < 1 {
		LogPc.Error(fmt.Sprintf("redis地址配置失败,请至少配一个地址,请检查配置"))
		return false
	}
	RedisConf = tmpRedis[0]
	LogPc.Debug(fmt.Sprintf("redis地址配置:%#v\n", RedisConf))

	//---------------------------------
	value = SystemConfInfoByKey(SysConfMongodb)
	if value == "" {
		LogPc.Error(fmt.Sprintf("mongodb地址配置不正确,或没有配置,请检查配置"))
		return false
	}
	LogPc.Debug(fmt.Sprintf("mongodb地址配置value:%#v\n", value))
	err = json.Unmarshal([]byte(value), &MgoConf)
	if err != nil {
		LogPc.Error(fmt.Sprintf("mongodb地址配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("mongodb地址配置:%#v\n", MgoConf))

	//--------------------
	value = SystemConfInfoByKey(SysConfQRCode)
	if value == "" {
		LogPc.Error(fmt.Sprintf("代理店铺及个人二维码配置不正确,或没有配置,请检查配置"))
		return false
	}
	err = json.Unmarshal([]byte(value), &QRCode)
	if err != nil {
		LogPc.Error(fmt.Sprintf("代理店铺及个人二维码配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("代理店铺及个人二维码配置:%#v\n", QRCode))
	//-----------------------------------------
	value = SystemConfInfoByKey(SysConfAgentCheck)
	if value == "" {
		LogPc.Error(fmt.Sprintf("代理考核功能是否开启配置不正确,或没有配置,请检查配置"))
		return false
	}
	AgentCheck, err = strconv.ParseBool(value)
	if err != nil {
		LogPc.Error(fmt.Sprintf("代理考核功能是否开启配置失败:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("代理考核功能是否开启配置:%#v\n", AgentCheck))
	//------------------------------------------------
	OssDomain = SystemConfInfoByKey(SysConfOssDomain)
	if OssDomain == "" {
		LogPc.Error(fmt.Sprintf("OSS下载域名配置不正确,或没有配置,请检查配置"))
		return false
	}
	LogPc.Debug(fmt.Sprintf("OSS下载域名开启配置:%#v\n", OssDomain))
	//------------------------------------------------
	if !InitBasePayCallBack() {
		return false
	}
	//-----------------以上是配置----------------------------------
	return true
}

func InitBasePayCallBack() bool {
	value := SystemConfInfoByKey(SysConfDomain)
	if value == "" {
		LogPc.Error(fmt.Sprintf("域名配置不正确,或没有配置,请检查配置"))
		return false
	}
	err := json.Unmarshal([]byte(value), &Domain)
	length := len(Domain)
	if err != nil || length == 0 {
		LogPc.Error(fmt.Sprintf("域名没有配置,或配置错误:%v,请检查配置", err))
		return false
	}
	LogPc.Debug(fmt.Sprintf("域名配置:%#v\n", Domain))
	var port int
	if PlatformConf.HttpPort == 0 {
		port = beego.BConfig.Listen.HTTPPort
	} else {
		port = PlatformConf.HttpPort
	}
	if length == 1 {
		BasePayCallBack = fmt.Sprintf("%v:%v", Domain[0], port)
	} else { //随机选择一个域名
		n := rand.Intn(length - 1)
		BasePayCallBack = fmt.Sprintf("%v:%v", Domain[n], port)
	}
	LogPc.Debug(fmt.Sprintf("支付回调基地址:%v", BasePayCallBack))
	return true
}

//获取支付方式
func PayTypeByPlatform(platformType int64, multiOrm ...orm.Ormer) ([]PayType, error) {
	var payType []PayType
	key := fmt.Sprintf("%v%v", KeyPayType, platformType)
	err := GetCache(MemCache, key, &payType)
	if err != nil { //doesn't exist,get it from mysql
		o := NewOrm(multiOrm, DBChessSysData)
		sql := fmt.Sprintf(`
			SELECT
				*
			FROM
				%v
			WHERE
				platform_type = %v
			`, TablePayType, platformType)
		_, err = o.Raw(sql).QueryRows(&payType)
		if CheckError(err) {
			return nil, err
		}
		//即使为空也缓存，为了减少因为数据库里不存在此数据而步骤与数据库交互,所以是用checkError而不是checkNoExist
		SetCache(MemCache, key, payType, CacheExpire)
	}
	return payType, nil
}

//结构体嵌套有结构体的不行
func SetCache(c cache.Cache, key string, value interface{}, timeout int) error {
	data, err := Encode(value)
	if err != nil {
		return err
	}
	timeouts := time.Duration(timeout) * time.Second
	return c.Put(key, data, timeouts)
}

//结构体嵌套有结构体的可以
func SetSliceCache(key string, value []orm.Params, timeout int64) error {
	if len(value) == 0 {
		Redis.HSet(key, "empty", "empty")
	} else {
		for k, v := range value {
			data, err := json.Marshal(v)
			if err != nil {
				return err
			}
			Redis.HSet(key, k, data)
		}
	}
	_, err := Redis.Expire(key, timeout)
	return err
}

func IsRedisCacheExist(key string) bool {
	flag, _ := Redis.Exists(key)
	return flag
}

func GetSliceCache(key string) (result []orm.Params, err error) {
	rawData, err := Redis.HGetAll(key)
	if err != nil {
		return
	}
	for _, v := range rawData {
		if v == "empty" {
			return
		}
		tmp := make(orm.Params)
		err := json.Unmarshal([]byte(v), &tmp)
		if err != nil {
			return result, err
		}
		result = append(result, tmp)
	}
	return
}

func GetCache(c cache.Cache, key string, to interface{}) error {
	data := c.Get(key)
	if data == nil {
		return errors.New("Cache不存在")
	}
	return Decode(data.([]byte), to)

}

func DelCache(c cache.Cache, key string) error {
	return c.Delete(key)
}

func IsExist(c cache.Cache, key string) bool {
	return c.IsExist(key)
}

// increase cached int value by key, as a counter.
func Incr(c cache.Cache, key string) error {
	return c.Incr(key)
}

// decrease cached int value by key, as a counter.
func Decr(c cache.Cache, key string) error {
	return c.Decr(key)
}

// clear all cache.
func ClearAll(c cache.Cache) error {
	return c.ClearAll()
}

// --------------------
// Encode
// 用gob进行数据编码
//
func Encode(data interface{}) ([]byte, error) {
	buf := bytes.NewBuffer(nil)
	enc := gob.NewEncoder(buf)
	err := enc.Encode(data)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// -------------------
// Decode
// 用gob进行数据解码
//
func Decode(data []byte, to interface{}) error {
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	return dec.Decode(to)
}
